﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using D3D = Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using System.Threading;

namespace CubePuzzle
{
    public class DxAppGame : DxApp
    {
        private MatrixBuilder mb;
        private int itemIndex;

        public int ItemIndex {
            set
            {
                itemIndex = value;
            }

            get
            {
                return itemIndex;
            }
        }

        public override void ReadKeyBoard()
        {
            KeyboardState keys = keyb.GetCurrentKeyboardState();
            if (itemIndex == 0)
            {
                if (keys[Key.LeftArrow])
                {
                    float tmpDirX = cameraUpVector.Y * cameraForward.Z - cameraUpVector.Z * cameraForward.Y;
                    float tmpDirY = cameraUpVector.X * cameraForward.Z - cameraUpVector.Z * cameraForward.X;
                    float tmpDirZ = cameraUpVector.X * cameraForward.Y - cameraUpVector.Y * cameraForward.X;
                    Vector3 tmpDirection = new Vector3(tmpDirX, tmpDirY, tmpDirZ);
                    cameraAt = cameraAt - 0.08f * tmpDirection;
                    cameraPosition = cameraPosition - 0.08f * tmpDirection;
                }

                if (keys[Key.RightArrow])
                {
                    float tmpDirX = cameraUpVector.Y * cameraForward.Z - cameraUpVector.Z * cameraForward.Y;
                    float tmpDirY = cameraUpVector.X * cameraForward.Z - cameraUpVector.Z * cameraForward.X;
                    float tmpDirZ = cameraUpVector.X * cameraForward.Y - cameraUpVector.Y * cameraForward.X;
                    Vector3 tmpDirection = new Vector3(tmpDirX, tmpDirY, tmpDirZ);
                    cameraAt = cameraAt + 0.08f * tmpDirection;
                    cameraPosition = cameraPosition + 0.08f * tmpDirection;
                }

                if (keys[Key.DownArrow])
                {
                    cameraPosition = cameraPosition - 0.08f * cameraForward;
                    cameraAt = cameraAt - 0.08f * cameraForward;
                }

                if (keys[Key.UpArrow])
                {
                    cameraPosition = cameraPosition + 0.08f * cameraForward;
                    cameraAt = cameraAt + 0.08f * cameraForward;
                }
            }
            else
            {
                if (keys[Key.LeftArrow])
                {
                    if ((Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Y)) && (Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Z)))
                    {
                        if (cameraForward.X < 0) mb.move(-3, itemIndex - 1);
                        else mb.move(3, itemIndex - 1);
                    }
                    else
                    {
                        if (cameraForward.Z < 0) mb.move(1, itemIndex - 1);
                        else mb.move(-1, itemIndex - 1);
                    }
                    Thread.Sleep(100);
                }

                if (keys[Key.RightArrow])
                {
                    if ((Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Y)) && (Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Z)))
                    {
                        if (cameraForward.X < 0) mb.move(3, itemIndex - 1);
                        else mb.move(-3, itemIndex - 1);
                    }    
                    else 
                    {
                        if (cameraForward.Z < 0) mb.move(-1, itemIndex - 1);
                        else mb.move(1, itemIndex - 1);
                    }
                    Thread.Sleep(100);
                }

                if (keys[Key.DownArrow])
                {
                    mb.move(-2, itemIndex - 1);
                    Thread.Sleep(200);
                }

                if (keys[Key.UpArrow])
                {
                    mb.move(2, itemIndex - 1);
                    Thread.Sleep(100);
                }

                if (keys[Key.PageDown])
                {
                    if ((Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Y)) && (Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Z)))
                    {
                        if (cameraForward.X < 0) mb.move(1, itemIndex - 1);
                        else mb.move(-1, itemIndex - 1);
                    } else {
                        if (cameraForward.Z < 0) mb.move(3, itemIndex - 1);
                        else mb.move(-3, itemIndex - 1);
                    }
                    Thread.Sleep(100);
                }

                if (keys[Key.PageUp])
                {
                    if ((Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Y)) && (Math.Abs(cameraForward.X) > Math.Abs(cameraForward.Z)))
                    {
                        if (cameraForward.X < 0) mb.move(-1, itemIndex - 1);
                        else mb.move(1, itemIndex - 1);
                    }
                    else
                    {
                        if (cameraForward.Z < 0) mb.move(-3, itemIndex - 1);
                        else mb.move(3, itemIndex - 1);
                       
                    }
                    Thread.Sleep(100);
                }

                if (keys[Key.A])
                {
                    mb.rotate(1, itemIndex - 1);
                    Thread.Sleep(100);
                }

                if (keys[Key.S])
                {
                    mb.rotate(2, itemIndex - 1);
                    Thread.Sleep(100);
                }

                if (keys[Key.D])
                {
                    mb.rotate(3, itemIndex - 1);
                    Thread.Sleep(100);
                }
            }
        }

        

        public override void ReadMouse()
        {
            currentMouseState = mouse.CurrentMouseState;

            if ((currentMouseState.X != 0 || (currentMouseState.Y != 0)))
            {
                v +=  ((float)Math.PI) * ((currentMouseState.Y) / (float)device.Viewport.Height);
                u += -2 * ((float)Math.PI) * ((currentMouseState.X) / (float)device.Viewport.Width);

                if (Math.Abs(v) > (float) 3*Math.PI / 4) v = (float) (3*Math.PI / 4);
                if (Math.Abs(v) < (float) Math.PI / 4) v = (float) Math.PI / 4;

                cameraForward.X = (float)(Math.Cos((double)u) * Math.Sin((double)v));
                cameraForward.Y = (float)Math.Cos((double)v);
                cameraForward.Z = (float)(Math.Sin((double)u) * Math.Sin((double)v));

                cameraAt.X = cameraPosition.X + cameraDistance * cameraForward.X;
                cameraAt.Y = cameraPosition.Y + cameraDistance * cameraForward.Y;
                cameraAt.Z = cameraPosition.Z + cameraDistance * cameraForward.Z;

            }
        }

        public override void UpdateCamera()
        {
            matView = Matrix.LookAtLH(cameraPosition, cameraAt, cameraUpVector);
            device.Transform.View = matView;
        }

        public void drawItem() { 

            int n = mb.N * mb.K;

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    for (int k = 0; k < n; k++)
                    {
                        if (mb.Matrix[i][j][k] > 0) 
                        {
                            effect.SetValue("xColoredTexture", cubeTextures[mb.Matrix[i][j][k] % 11]);
                            matWorld.Translate(new Vector3(i, j, k));
                            device.Transform.World = matWorld;
                            effect.SetValue("xWorldViewProjection", matWorld * matView * matProjection);
                            effect.SetValue("xLightPos", new Vector4(cameraPosition.X, cameraPosition.Y, cameraPosition.Z, 1));
                            effect.SetValue("xLightPower", 2.0f);
                            effect.CommitChanges();
                            device.DrawPrimitives(PrimitiveType.TriangleFan, 0, 2);
                            device.DrawPrimitives(PrimitiveType.TriangleFan, 4, 2);
                            device.DrawPrimitives(PrimitiveType.TriangleFan, 8, 2);
                            device.DrawPrimitives(PrimitiveType.TriangleFan, 12, 2);
                            device.DrawPrimitives(PrimitiveType.TriangleFan, 16, 2);
                            device.DrawPrimitives(PrimitiveType.TriangleFan, 20, 2);
                        }
                    }
                }
            }
        }

        public override void SetUpCamera()
        {
            cameraPosition = new Vector3(mb.K * mb.N / 2, mb.K * mb.N / 2, mb.K * mb.N * 2);

            cameraForward = new Vector3(0, 0, -1);

            cameraAt = new Vector3(mb.K * mb.N / 2, mb.K * mb.N / 2, 0);

            cameraUpVector = new Vector3(0, 1, 0);

            cameraDistance = mb.K * mb.N * 2;

            matProjection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float) form.PbGame.Width / (float) form.PbGame.Height, 1f, 100f);

            matView = Matrix.LookAtLH(cameraPosition, cameraAt, cameraUpVector);

            matWorld = Matrix.Identity;

            device.Transform.Projection = matProjection;
            device.Transform.View = matView;
            device.Transform.World = matWorld;
        }

        public override void Render()
        {
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.LightSkyBlue, 1.0f, 0);
            device.BeginScene();

            UpdateCamera();

            device.SetStreamSource(0, vb, 0);
            device.VertexDeclaration = vd;
            effect.Technique = "Simplest";
            effect.SetValue("xRot", Matrix.Identity);
            int numpasses = effect.Begin(0);
            for (int ii = 0; ii < numpasses; ii++)
            {
                effect.BeginPass(ii);
                drawItem();           
                effect.EndPass();
            }
            effect.End();
                 

            // End the scene
            device.EndScene();
            device.Present();
            form.Invalidate();

            if (form.CbGame.Checked)
            {
                ReadKeyBoard();
                ReadMouse();
            }
            else
                currentMouseState = mouse.CurrentMouseState;
        }

        public override void HandleDeviceReset(object sender, EventArgs e)
        {
            device.RenderState.CullMode = Cull.None;
            UpdateCamera();
            FillResources();
        }

        public override void InitTextures()
        {
            cubeTextures = new Texture[11];

            for (int i = 0; i < 11; i++)
            {
                cubeTextures[i] = TextureLoader.FromFile(device, "textures\\" + i.ToString() + ".bmp");
            }
        }

        public DxAppGame(IntPtr handle, Game form, MatrixBuilder mb)
        {
            this.mb = mb;
            this.form = form;
            InitializeDevice(handle);
            AllocateResources();
            FillResources();
            InitializeKeyboard();
            InitializeMouse();
            InitTextures();
        }
    }
}
